package cron

import (
	"github.com/robfig/cron/v3"
	"log"
	"strconv"
	"strings"
	"time"
	"xiaoniu-job/internal"
	"xiaoniu-job/internal/client"
	"xiaoniu-job/internal/db"
	"xiaoniu-job/pkg"
)

var c *cron.Cron

func Init() {
	c = cron.New(cron.WithSeconds())
	c.Start()
	select {}
}

func DoUpdateTaskCronJob() {
	for data := range internal.UpdateTaskChan {
		RemoveTaskCron(data)
	}
}

/**
调度任务的主要逻辑
*/

// 保存任务与entryId的对应关系
var taskEntryIdMap = make(map[string]cron.EntryID)

// 保存任务与cron表达式的对应关系
var taskCronMap = make(map[string]string)

// DoBackGroundJob 根据开启任务的cron表达式定时向客户端发送执行任务的请求
func DoBackGroundJob() {
	for {
		tasks := db.GetStartingTask()
		//fmt.Println("查询到任务数：", len(tasks))
		for k, _ := range tasks {
			go AddTaskCron(&tasks[k])
		}
		// 每一秒钟执行一次
		time.Sleep(time.Second * 1)
	}
}

// AddTaskCron 开启一个定时任务
func AddTaskCron(task *pkg.Task) {
	if task == nil || task.Cron == "" {
		return
	}
	key := strconv.FormatUint(task.Id, 10) + ":" + strconv.FormatUint(task.ExecutorId, 10)
	_, ok := taskEntryIdMap[key]
	// 如果任务已经存在
	if ok {
		// 判断cron表达式有没有变化
		if cron, exists := taskCronMap[key]; exists {
			if task.Cron != cron {
				go UpdateTaskCron(task)
			}
		}
		return
	}
	go NewTaskCron(key, task)
}

func NewTaskCron(key string, task *pkg.Task) {
	entryID, _ := c.AddFunc(task.Cron, func() {
		go SendTaskToClient(task, 0, "", "")
	})
	taskEntryIdMap[key] = entryID
	taskCronMap[key] = task.Cron
}

// SendTaskToClient 区分自动触发还是手动执行 triggerType = 0 自动触发 1 手动执行
func SendTaskToClient(task *pkg.Task, triggerType int8, customTaskParam string, customExecutorAddr string) {
	// 获取该任务对应的执行器信息
	if task == nil {
		return
	}
	log.Println("======开始执行任务=====：", task.TaskName, task.TaskLabel)

	var taskParam = task.TaskParam
	var executorAddr = ""
	// 手动触发的话任务参数和执行器地址如果不为空就取
	if triggerType == 1 && customTaskParam != "" {
		taskParam = customTaskParam
	}
	if triggerType == 1 && customExecutorAddr != "" {
		executorAddr = customExecutorAddr
	}
	executor, _ := db.GetExecutorById(task.ExecutorId)

	executoName := ""
	if executor != nil {
		executoName = executor.Name
	}
	if executor == nil || (executor.Addresses == "" && executorAddr == "") {
		var jobLog = &pkg.JobLog{
			TaskId:          task.Id,
			TaskName:        task.TaskName,
			ExecutorName:    executoName,
			ExecutorAddress: "",
			StartTime:       time.Now(),
			EndTime:         time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC),
			Status:          int8(2),
			ScheduleResult:  "执行器地址为空",
			Result:          "执行初始化",
			TaskParam:       taskParam,
			TriggerType:     triggerType,
		}
		res, err := db.AddJobLog(jobLog)
		if res == 0 || err != nil {
			log.Println("执行器地址为空，创建JobLog出错: ", res, err)
		}
		return
	}
	// 默认取第一个执行器地址
	split := strings.Split(executor.Addresses, ",")
	if executorAddr == "" {
		executorAddr = split[0]
	}

	// 新增一条执行日志到数据库
	var jobLog = &pkg.JobLog{
		TaskId:          task.Id,
		TaskName:        task.TaskName,
		ExecutorName:    executor.Name,
		ExecutorAddress: executorAddr,
		StartTime:       time.Now(),
		EndTime:         time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC),
		Status:          int8(0),
		ScheduleResult:  "调度初始化",
		Result:          "执行初始化",
		TaskParam:       taskParam,
		TriggerType:     triggerType,
	}
	res, err := db.AddJobLog(jobLog)
	if res == 0 || err != nil {
		log.Println("创建JobLog出错: ", res, err)
		return
	}

	// 向客户端发送执行任务的请求
	sendTaskRes, err := client.SendTask(jobLog.Id, task.TaskLabel, executorAddr, taskParam)
	if err != nil {
		jobLog.Status = 2
	} else {
		jobLog.Status = 1
	}
	jobLog.ScheduleResult = sendTaskRes

	updateJobLogCnt, err := db.UpdateJobLog(jobLog)
	if updateJobLogCnt == 0 {
		log.Fatalln("执行日志更新失败", err, jobLog)
	}
}

// RemoveTaskCron 删除一个定时任务的调度策略，也就是一个任务从开启转为关闭中触发
func RemoveTaskCron(task *pkg.Task) {
	if task == nil || task.Cron == "" || task.Status == 1 {
		return
	}
	key := strconv.FormatUint(task.Id, 10) + ":" + strconv.FormatUint(task.ExecutorId, 10)
	entry, ok := taskEntryIdMap[key]
	// 如果任务已经存在
	if ok {
		// 停止该定时任务
		c.Remove(entry)
		// 从map中删除
		delete(taskEntryIdMap, key)
		delete(taskCronMap, key)
	}
}

// UpdateTaskCron 更新一个定时任务的调度策略
func UpdateTaskCron(task *pkg.Task) {
	if task == nil || task.Cron == "" {
		return
	}
	key := strconv.FormatUint(task.Id, 10) + ":" + strconv.FormatUint(task.ExecutorId, 10)
	entry, ok := taskEntryIdMap[key]
	// 如果任务已经存在
	if ok {
		// 停止该定时任务
		c.Remove(entry)
	}
	entryID, _ := c.AddFunc(task.Cron, func() {
		go SendTaskToClient(task, 0, "", "")
	})
	taskEntryIdMap[key] = entryID
	taskCronMap[key] = task.Cron
}
